
#include "SQLBinaryOpExprGroup.h"
#include "SQLBinaryOpExpr.h"

#include "../../../utils/instanceof.h"
#include "../../../utils/ifeq.h"
#include <set>

SETCALSS(SQLBinaryOpExprGroup);

SQLBinaryOpExprGroup::SQLBinaryOpExprGroup(SQLBinaryOperator_ptr operator_)
{
  items = std::make_shared<std::list<SQLExpr_ptr>>();
  this->operator_ = operator_;
}

SQLBinaryOpExprGroup::SQLBinaryOpExprGroup(SQLBinaryOperator_ptr operator_, DbType_ptr dbType)
{
  items = std::make_shared<std::list<SQLExpr_ptr>>();
  this->operator_ = operator_;
  this->dbType = dbType;
}

bool SQLBinaryOpExprGroup::equals(Object_ptr o)
{
  if (this == o.get())
  {
    return true;
  }
  if (o == NULL || getClass() != o->getClass())
  {
    return false;
  }

  SQLBinaryOpExprGroup_ptr that = std::dynamic_pointer_cast<SQLBinaryOpExprGroup>(o);

  if (operator_ != that->operator_)
  {
    return false;
  }
  return IFEQ::ifeq<SQLExpr_ptr>( items,that->items);
}

int SQLBinaryOpExprGroup::hashCode()
{
  // int result = operator_ != NULL ? operator_->hashCode() : 0;
  // result = 31 * result + items->hashCode();
  // return result;
  return 0;
}

void SQLBinaryOpExprGroup::accept0(SQLASTVisitor_ptr visitor)
{
  if (visitor->visit(SharedObject(SQLBinaryOpExprGroup)))
  {
    auto it_items = this->items->begin();
    for (int i = 0; i < this->items->size(); i++)
    {
      std::advance(it_items, i);
      SQLExpr_ptr item = *it_items;
      item->accept(visitor);
    }
  }

  visitor->endVisit(SharedObject(SQLBinaryOpExprGroup));
}

SQLObject_ptr SQLBinaryOpExprGroup::clone()
{
  SQLObject_ptr clone_tmp = SQLBinaryOpExprGroup_ptr(new SQLBinaryOpExprGroup(operator_));
  SQLBinaryOpExprGroup_ptr x = std::dynamic_pointer_cast<SQLBinaryOpExprGroup>(clone_tmp);
  // SQLBinaryOpExprGroup *x = new SQLBinaryOpExprGroup(operator_);

  for (SQLExpr_ptr item : *items)
  {
    SQLExpr_ptr item2 = std::dynamic_pointer_cast<SQLExpr>(item->clone());
    item2->setParent(SharedObject(SQLBinaryOpExprGroup));
    x->items->push_back(item2);
  }

  return x;
}

SQLObject_list_ptr SQLBinaryOpExprGroup::getChildren()
{
  SQLObject_list_ptr newClass = SQLObject_list_ptr();
  for (auto it : *this->items)
  {
    newClass->push_back(std::dynamic_pointer_cast<SQLObject>(it));
  }
  return newClass;

  // return items;
}

void SQLBinaryOpExprGroup::add(SQLExpr_ptr item)
{
  add(items->size(), item);
}

void SQLBinaryOpExprGroup::add(int index, SQLExpr_ptr item)
{
  if (instanceof <SQLExpr, SQLBinaryOpExpr>(item))
  {
    SQLBinaryOpExpr_ptr binaryOpExpr = std::dynamic_pointer_cast<SQLBinaryOpExpr>(item);
    if (binaryOpExpr->getOperator() == operator_)
    {
      add(binaryOpExpr->getLeft());
      add(binaryOpExpr->getRight());
      return;
    }
  }
  else if (instanceof <SQLExpr, SQLBinaryOpExprGroup>(item))
  {
    SQLBinaryOpExprGroup_ptr group = std::dynamic_pointer_cast<SQLBinaryOpExprGroup>(item);
    if (group->operator_ == this->operator_)
    {
      for (SQLExpr_ptr sqlExpr : *group->getItems())
      {
        add(sqlExpr);
      }
      return;
    }
  }

  if (item != NULL)
  {
    item->setParent(SharedObject(SQLBinaryOpExprGroup));
  }
  auto iter = this->items->begin();
  std::advance(iter, index);
  this->items->insert(iter, 1, item);
}

SQLExpr_list_ptr SQLBinaryOpExprGroup::getItems()
{
  return this->items;
}

SQLBinaryOperator_ptr SQLBinaryOpExprGroup::getOperator()
{
  return operator_;
}

string_ptr SQLBinaryOpExprGroup::toString()
{
  string_ptr tmp = SQLUtils::toSQLString(std::dynamic_pointer_cast<SQLObject>(SharedObject(SQLBinaryOpExprGroup)), dbType);
  return (tmp);
}

BOOL_ptr SQLBinaryOpExprGroup::replace(SQLExpr_ptr expr, SQLExpr_ptr target)
{
  BOOL_ptr replaced = BOOL::FALSE;
  auto it_items = items->begin();
  for (int i = 0; i < items->size(); i++)
  {
    std::advance(it_items, i);
    if ((*it_items) == expr)
    {
      if (target == NULL)
      {
        items->erase(it_items);
      }
      else
      {
        if (instanceof <SQLExpr, SQLBinaryOpExpr>(target) && (std::dynamic_pointer_cast<SQLBinaryOpExpr>(target))->getOperator() == operator_)
        {
          items->erase(it_items);
          SQLExpr_list_ptr list = SQLBinaryOpExpr::split(target, operator_);
          auto it_list = list->begin();
          for (int j = 0; j < list->size(); j++)
          {
            std::advance(it_list, j);
            SQLExpr_ptr o = (*it_list);
            o->setParent(SharedObject(SQLBinaryOpExprGroup));
            items->push_back(o);
          }
        }
        else
        {
          target->setParent(SharedObject(SQLBinaryOpExprGroup));
          (*it_items) = target;
        }
      }
      replaced = BOOL::TRUE;
    }
  }

  std::advance(it_items, 0);
  if (items->size() == 1 && replaced)
  {
    SQLUtils::replaceInParent(std::dynamic_pointer_cast<SQLBinaryOpExprGroup>(SharedObject(SQLBinaryOpExprGroup)), (*it_items));
  }

  if (items->empty())
  {
    SQLUtils::replaceInParent(std::dynamic_pointer_cast<SQLBinaryOpExprGroup>(SharedObject(SQLBinaryOpExprGroup)), NULL);
  }

  return replaced;
}

void SQLBinaryOpExprGroup::optimize()
{
  std::shared_ptr<std::list<int>> dupIndexList = std::make_shared<std::list<int>>();

  std::set<SQLExpr_ptr> itemSet;
  auto it_items = items->begin();
  for (int i = 0; i < items->size(); i++)
  {
    std::advance(it_items, i);
    itemSet.insert(*it_items);
    // if (!itemSet.insert(*it_items))
    // {
    // if (dupIndexList.empty())
    // {
    //   dupIndexList = new ArrayList<Integer>();
    // }
    //   dupIndexList.push_back(i);
    // }
  }

  if (!dupIndexList->empty())
  {
    auto it_dupIndexList = dupIndexList->begin();
    for (int i = dupIndexList->size() - 1; i >= 0; i--)
    {
      std::advance(it_dupIndexList, i);
      int index = (*it_dupIndexList);
      std::advance(it_items, index);
      items->erase(it_items);
    }
  }
}
